﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Windows.Forms;
using System.Reflection;

namespace Fonlow.CommandLineGui
{
    /// <summary>
    /// Build optional parameters in a line, separated by single spaces.
    /// </summary>
    public class OptionsBuilder
    {
        public OptionsBuilder(object options)
        {
            if (options == null)
            {
                throw new ArgumentNullException("options");
            }
            this.options = options;
            propertiesArray = options.GetType().GetProperties();
        }

        object options;

        /// <summary>
        /// All properties of options.
        /// </summary>
        PropertyInfo[] propertiesArray;

        /// <summary>
        /// Get default value of a property.
        /// </summary>
        /// <param name="propertyInfo"></param>
        /// <returns>Null if the default value is not defined. Empty string if the type is string. 0 if the type is int.</returns>
        static object GetDefaultValue(PropertyInfo propertyInfo)
        {
            object[] objects = propertyInfo.GetCustomAttributes(typeof(DefaultValueAttribute), false);
            if (objects.Length == 1)
            {
                return (objects[0] as DefaultValueAttribute).Value;
            }

            if (propertyInfo.PropertyType == typeof(string))
            {
                return String.Empty;
            }

            if (propertyInfo.PropertyType == typeof(int))
            {
                return 0;
            }

            return null;
        }

        /// <summary>
        /// Get display name of a property
        /// </summary>
        /// <param name="propertyInfo"></param>
        /// <returns>If DisplayNameAttribute does not exist, the property name will be used.</returns>
        static string GetDisplayName(PropertyInfo propertyInfo)
        {
            object[] objects = propertyInfo.GetCustomAttributes(typeof(DisplayNameAttribute), false);
            if (objects.Length == 1)
            {
                return (objects[0] as DisplayNameAttribute).DisplayName;
            }
            return propertyInfo.Name;
        }

        static bool GetNameValueNoSpace(PropertyInfo propertyInfo)
        {
            object[] objects = propertyInfo.GetCustomAttributes(typeof(NameValueNoSpaceAttribute), false);
            if (objects.Length == 1)
            {
                return true;
            }
            return false;
        }



        static TypeConverterAttribute GetTypeConverterAttribute(Type t)
        {
            AttributeCollection attributeCollection = TypeDescriptor.GetAttributes(t);
            return attributeCollection[typeof(TypeConverterAttribute)] as TypeConverterAttribute;
        }

        static TypeConverter GetTypeConverter(Type t)
        {
            TypeConverterAttribute converterAttribute = GetTypeConverterAttribute(t);
            if ((converterAttribute != null)&&(! String.IsNullOrEmpty(converterAttribute.ConverterTypeName)))
            {
                Type converterType = Type.GetType(converterAttribute.ConverterTypeName);
                if (converterType != null)
                {
                    TypeConverter converter = Activator.CreateInstance(converterType) as TypeConverter;
                    return converter;
                }
            }

            return null;
        }

        /// <summary>
        /// return a list of parameters from modified properties.
        /// </summary>
        /// <returns></returns>
        public IList<string> DefinedParameters
        {
            get
            {
                List<string> parameters = new List<string>();
                foreach (PropertyInfo p in propertiesArray)
                {
                    object defaultValue = GetDefaultValue(p);
                    object currentValue = p.GetValue(options, null);

                    bool noDefaultValueSoOK = (defaultValue == null) && (currentValue != null);
                    if (noDefaultValueSoOK)
                    {
                        parameters.Add(GetDisplayName(p) + Convert.ToString(currentValue));
                    }
                    else
                    {
                        bool propertyModified = (currentValue != null) && (defaultValue.ToString() != currentValue.ToString());
                        if (propertyModified)
                        {
                            if (p.PropertyType == typeof(bool))
                            {
                                parameters.Add(GetDisplayName(p));
                            }
                            else
                            {
                                TypeConverter converter = GetTypeConverter(currentValue.GetType());
                                string spaceMaybe = GetNameValueNoSpace(p) ? String.Empty : " ";
                                if (converter != null)// The property is decorated with TypeConverterAttribute
                                {
                                    parameters.Add(GetDisplayName(p) + spaceMaybe + converter.ConvertToString(currentValue));
                                }
                                else
                                    parameters.Add(GetDisplayName(p) + spaceMaybe + Convert.ToString(currentValue));
                            }
                        }
                    }
                }
                return parameters;
            }
        }
        /// <summary>
        /// Get parameters in a line separated by single spaces.
        /// </summary>
        /// <returns></returns>
        public string ParametersLine
        {
            get
            {
                IList<string> parameters = DefinedParameters;
                StringBuilder builder = new StringBuilder();
                foreach (string s in parameters)
                {
                    builder.Append(s + " ");
                }
                return builder.ToString();
            }
        }
    }
}
